﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using NetSurveillance.WebApp.DataServices.Models;

namespace NetSurveillance.WebApp.Utilities
{
    public static class ReflectionUtility
    {
        public static string GetPropertyName<T>(Expression<Func<T>> expression)
        {
            var body = expression.Body as MemberExpression;
            if(body == null)
                throw new ArgumentException("'expression' should be a member expression");
            return body.Member.Name;
        }

        public static Expression<Func<TModel, TValue>> GenerateMemberExpr<TModel,TValue>(
            string propertyName)
        {
            var tmodelType = typeof (TModel);
            var propertyInfo = tmodelType.GetProperty(propertyName);
            var entityParam = Expression.Parameter(tmodelType, "m");
            Expression columnExpr = Expression.Property(entityParam, propertyInfo);
            var tvalueType = typeof (TValue);
            if (propertyInfo.PropertyType != tvalueType)
                columnExpr = Expression.Convert(columnExpr, tvalueType);
            return Expression.Lambda<Func<TModel, TValue>>(columnExpr, entityParam);
        }

        /// <summary>
        /// 将源对象中与目标对象中同名同类属性合并(拷贝源对象中的到目标对象中)
        /// </summary>
        /// <example>将A对象中的值拷贝到B对象 : B.MergeFrom(A);</example>
        /// <typeparam name="T">源类型</typeparam>
        /// <param name="destination">目标对象</param>
        /// <param name="source">源对象</param>
        /// <param name="excludeFields">排除字段</param>
        public static void MergeFrom<T>(this object destination, T source, string[] excludeFields = null)
        {
            Type destinationType = destination.GetType();
            PropertyInfo[] propertyInfos = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var propertyInfo in propertyInfos)
            {
                PropertyInfo destinationPropertyInfo = destinationType.GetProperty(propertyInfo.Name,
                                                                                   BindingFlags.Public |
                                                                                   BindingFlags.Instance);
                if (destinationPropertyInfo == null || !destinationPropertyInfo.CanWrite || !propertyInfo.CanRead ||
                    (destinationPropertyInfo.PropertyType != propertyInfo.PropertyType) ||
                    (excludeFields != null && excludeFields.Contains(propertyInfo.Name))) continue;
                object o = propertyInfo.GetValue(source, null);
                destinationPropertyInfo.SetValue(destination, o, null);
            }
        }

        /// <summary>
        /// 将源对象中对于需要显示的字段取出生成字典数据
        /// </summary>
        /// <typeparam name="T">源对象类型</typeparam>
        /// <param name="model">源对象</param>
        /// <param name="displayFields">需要显示的字段:key为字段名，value为需要显示的字段名称</param>
        /// <returns></returns>
        public static Dictionary<string, string> ConvertModelToDic<T>(T model,
            Dictionary<string, string> displayFields) where T : class 
        {
            if (model == null) return null;
            var drDic = new Dictionary<string, string>();
            var drResultType = typeof(T);
            if (displayFields == null || !displayFields.Any())
            {
                var allProperties = drResultType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (var propertyInfo in allProperties)
                {
                    object propertyVal = propertyInfo.GetValue(model, null);
                    drDic.Add(propertyInfo.Name, propertyVal == null ? null : propertyVal.ToString());
                }
            }
            else
            {
                foreach (var displayField in displayFields)
                {
                    var displayProperty = drResultType.GetProperty(displayField.Key);
                    object displayPropertyVal = displayProperty.GetValue(model, null);
                    drDic.Add(displayField.Value, displayPropertyVal == null ? null : displayPropertyVal.ToString());
                }
            }
            return drDic;
        }
    }
}